package com.chatplus.application.service.file.impl;

import cn.hutool.core.img.Img;
import cn.hutool.core.img.ImgUtil;
import com.chatplus.application.common.exception.BadRequestException;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.domain.entity.file.FileEntity;
import com.chatplus.application.domain.model.ImageScale;
import com.chatplus.application.enumeration.ImgOperateType;
import com.chatplus.application.service.file.FileService;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Service
public class ImgHandler {

    private static final SouthernQuietLogger log = SouthernQuietLoggerFactory.getLogger(ImgHandler.class);

    private static final String LOGO_PATH = "classpath:logo/";

    private static final String DEFAULT_LOGO_PC_BIG = "logo.png";

    private final ResourceLoader resourceLoader;

    public ImgHandler(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    //由于logo图片数量较少，缓存到内存，减少读盘
    private static final Map<String, BufferedImage> logoMap = new ConcurrentHashMap<>(8);

    /**
     * 获取logo图片
     *
     * @param logoImgName logo图片名称
     * @return Image
     */
    public BufferedImage getLogo(String logoImgName) {

        if (StringUtils.isBlank(logoImgName)) {
            return null;
        }
        BufferedImage logoImage = logoMap.get(logoImgName);
        if (logoImage != null) {
            return logoImage;
        }
        //使用ResourcePatternUtils避免文件在jar包中无法读取
        Resource resource = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResource(LOGO_PATH + logoImgName);
        try {
            logoImage = ImageIO.read(resource.getInputStream());
            logoMap.put(logoImgName, logoImage);
        } catch (IOException e) {
            log.message("获取logo图片失败")
                    .context("logoImgName", logoImgName)
                    .exception(e).error();
        }
        return logoImage;
    }

//    public BufferedImage createWatermarkImg(InputStream imgInput) throws IOException {
//        if (imgInput != null && imgInput.available() > 0) {
//            BufferedImage originalImage = ImageIO.read(imgInput);
//            BufferedImage logo = this.getLogo(DEFAULT_LOGO_PC_BIG);
//            return Thumbnails.of(originalImage)
//                    //必须设置大小，否则有size not set的ERROR
//                    .size(originalImage.getWidth(), originalImage.getHeight())
//                    //var3表示透明度
//                    .watermark(Positions.BOTTOM_RIGHT, logo, 0.2f)
//                    .outputQuality(0.8)
//                    //缓存输出
//                    .asBufferedImage();
//        }
//        return null;
//    }
    public BufferedImage createWatermarkImg(InputStream imgInput) throws IOException {
        if (imgInput != null && imgInput.available() > 0) {
            Img img = Img.from(imgInput);
            Image logo = this.getLogo(DEFAULT_LOGO_PC_BIG);
            Img watermarkImg = img.pressImage(logo, this.createWatermarkRectangle(logo, img.getImg()), 1);
            Image image = watermarkImg.getImg();
            return ImgUtil.toBufferedImage(image);
        }
        return null;
    }
    private Rectangle createWatermarkRectangle(Image logo, Image srcImg) {
        int x;
        int y;
        int with = logo.getWidth(null);
        int height = logo.getHeight(null);
        int xGap = 5;//和左边框的间隔
        int yGap = 5;//和底部的间隔

        int srcImgWith = srcImg.getWidth(null);
        int srcImHeight = srcImg.getHeight(null);

        height = (srcImgWith * height) / (with * 3);
        xGap = (srcImgWith * xGap) / (with * 3);
        yGap = (srcImgWith * yGap) / (with * 3);
        with = srcImgWith / 3;
        x = -1 * (srcImgWith / 2) + (with / 2) + xGap;
        y = (srcImHeight / 2) - (height / 2) - yGap;
        Rectangle rectangle = new Rectangle();
        rectangle.setBounds(x, y, with, height);
        return rectangle;
    }

    /**
     * 获取水印图片/缩略图(默认1/5比例)
     *
     * @param fileEntity 图片对象
     * @param scale        缩略图尺寸
     * @param operateType  类型
     * @param thumbRatio   图片缩放比例 默认0.2
     * @return 图片流
     */
    @SuppressWarnings({"SameParameterValue", "unused"})
    public InputStream watermarkImage(FileEntity fileEntity,
                                      ImageScale scale,
                                      ImgOperateType operateType,
                                      Double thumbRatio,
                                      FileService fileService) {
        try (InputStream inputStream = fileService.getInputStream(fileEntity.getId())) {
            InputStream resultStream = new ByteArrayInputStream(StreamUtils.copyToByteArray(inputStream));
            String mediaType = fileEntity.getContentType();
            resultStream.reset();
            if (StringUtils.isEmpty(mediaType) || !mediaType.startsWith("image")) {
                throw new BadRequestException("文件类型不是图片");
            }
            String subType = mediaType.split("/")[1];
            if (null != scale) {
                BufferedImage image = scale(resultStream, scale.getWidth(), scale.getHeight());
                try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                    ImageIO.write(image, subType, outputStream);
                    resultStream.close();
                    resultStream = new ByteArrayInputStream(outputStream.toByteArray());
                }
            }
            //是否加水印
            if (ImgOperateType.WATER_MARK.equals(operateType)) {
                //生成水印图
                BufferedImage watermarkImg = createWatermarkImg(resultStream);
                //保存水印图片
                try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                    ImageIO.write(watermarkImg, subType, outputStream);
                    resultStream.close();
                    resultStream = new ByteArrayInputStream(outputStream.toByteArray());
                }
                resultStream.reset();
            } else if (ImgOperateType.THUMB.equals(operateType) && (resultStream.available() > 5 * 1024)) {
                try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                    double ratio = thumbRatio == null ? 0.2 : thumbRatio;

                    Thumbnails.of(resultStream).scale(ratio).toOutputStream(outputStream);
                    resultStream.close();
                    resultStream = new ByteArrayInputStream(outputStream.toByteArray());
                } catch (Exception ignore) {
                    //缩略失败则直接输出原图
                }
                resultStream.reset();

            }
            return resultStream;
        } catch (Exception e) {
            log.message("获取图片失败")
                    .context("fileEntity", fileEntity)
                    .exception(e).error();
            throw new BadRequestException("获取图片失败");
        }
    }

    public BufferedImage scale(InputStream resultStream, Integer width, Integer height) {
        Image srcImg = Img.from(resultStream).getImg();
        Image targetImg = null;
        float srcImgWith = srcImg.getWidth(null);//原图宽度
        float srcImHeight = srcImg.getHeight(null);//原图高度
        float scale = 0;//缩放比例
        boolean isCut = false;//是否需要裁剪
        //如果宽或高没有值，则等比例缩放
        if (width == null || width == 0) {
            width = 0;
            scale = height / srcImHeight;
        }
        //如果宽或高没有值，则等比例缩放
        if (height == null || height == 0) {
            height = 0;
            scale = width / srcImgWith;
        }
        //如果scale==0，说明需要指定宽高的图片
        if (scale == 0) {
            //如果比例一致，直接返回缩略图
            if (isSameScale(srcImgWith, srcImHeight, width, height)) {
                targetImg = ImgUtil.scale(srcImg, width, height);
            } else {
                //比例不一致需要裁剪
                isCut = true;
                scale = calculationScale(srcImgWith, srcImHeight, width, height);
            }
        }
        if (scale != 0) {
            targetImg = ImgUtil.scale(srcImg, scale);
        }
        //判断是否需要裁剪
        if (isCut && (targetImg != null)) {
            targetImg = ImgUtil.cut(targetImg, calculationRectangle(targetImg, width, height));

        }

        if (targetImg != null) {
            return ImgUtil.toBufferedImage(targetImg);
        }
        return null;
    }

    /**
     * 判断是否相同比例
     *
     * @param srcImgWith  原图宽度
     * @param srcImHeight 原图高度
     * @param width       放缩的宽度
     * @param height      放缩的高度
     * @return 是否相同比例
     */
    private boolean isSameScale(float srcImgWith, float srcImHeight, Integer width, Integer height) {
        float inaccuracy = 0.1f;
        //如果比例小于误差值认为相同
        if (srcImgWith > srcImHeight) {
            return Math.abs((srcImHeight / srcImgWith) - (height * 1f / width)) < inaccuracy;
        } else {
            return Math.abs((srcImgWith / srcImHeight) - (width * 1f / height)) < inaccuracy;
        }
    }

    /**
     * 计算放缩比例
     *
     * @param srcImgWith  原图宽度
     * @param srcImHeight 原图高度
     * @param width       放缩宽度
     * @param height      放缩高度
     * @return 返回放缩的比例
     */
    private float calculationScale(float srcImgWith, float srcImHeight, Integer width, Integer height) {
        float scaleW = width / srcImgWith;
        float scaleH = height / srcImHeight;
        return Math.max(scaleW, scaleH);
    }

    /**
     * 已中心点裁剪
     *
     * @param targetImg 需要裁剪的图片
     * @param width     放缩的宽度
     * @param height    放缩的高度
     * @return 返回图片显示的矩形
     */
    private Rectangle calculationRectangle(Image targetImg, Integer width, Integer height) {
        int x = (targetImg.getWidth(null) / 2);
        int y = (targetImg.getHeight(null) / 2);
        x = x - width / 2;
        y = y - height / 2;
        if (x < 0) {
            x = 0;
        }
        if (y < 0) {
            y = 0;
        }
        Rectangle rectangle = new Rectangle();
        rectangle.setBounds(x, y, width, height);
        return rectangle;
    }
}
